# ============================================================================
# 时间序列流量预测服务 - Flask REST API
# ============================================================================
# 
# 功能概述：
#   提供基于机器学习的时间序列预测 RESTful API 服务，支持多种预测模型
#   用于预测接口调用量、流量趋势，支持动态限流和资源调度
#
# 核心功能：
#   1. 单接口预测 (/api/forecast)
#   2. 批量预测 (/api/batch-forecast)
#   3. 模型评估 (/api/evaluate)
#   4. 健康检查 (/health)
#
# 支持的预测模型：
#   - Prophet: Facebook 时间序列预测模型（推荐，适合大多数场景）
#   - LSTM: 深度学习模型（适合复杂非线性模式）
#   - ARIMA: 传统统计模型（适合平稳时间序列）
#   - Ensemble: 集成学习模型（综合多个模型的优点）
#
# 技术栈：
#   - Flask: Web 框架
#   - Prophet: 时间序列预测
#   - Pandas/Numpy: 数据处理
#   - Flask-CORS: 跨域支持
#
# 作者: API Backend Team
# 版本: 1.0.0
# ============================================================================

# ----------------------------------------------------------------------------
# 标准库导入
# ----------------------------------------------------------------------------
from datetime import datetime, timedelta
import logging
import traceback

# ----------------------------------------------------------------------------
# 第三方库导入
# ----------------------------------------------------------------------------
from flask import Flask, request, jsonify
from flask_cors import CORS  # 跨域资源共享，允许前端调用
import pandas as pd
import numpy as np

# ----------------------------------------------------------------------------
# 自定义模块导入
# ----------------------------------------------------------------------------
# 预测模型
from models.prophet_model import ProphetModel      # Facebook Prophet 模型
from models.lstm_model import LSTMModel            # LSTM 深度学习模型
from models.arima_model import ARIMAModel          # ARIMA 统计模型
from models.ensemble import EnsembleModel          # 集成学习模型

# 工具类
from utils.data_preprocessor import DataPreprocessor  # 数据预处理器
from utils.evaluator import ModelEvaluator            # 模型评估器


# ============================================================================
# Flask 应用初始化
# ============================================================================

# 创建 Flask 应用实例
app = Flask(__name__)

# 启用 CORS（跨域资源共享）
# 说明：允许来自不同域名的前端应用调用此 API
# 适用场景：前后端分离架构、微服务架构
CORS(app)


# ============================================================================
# 日志配置
# ============================================================================

# 配置日志记录器
# 日志级别: INFO - 记录关键操作和正常运行信息
# 日志格式: 时间 - 模块名 - 级别 - 消息
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# 创建当前模块的日志记录器
logger = logging.getLogger(__name__)


# ============================================================================
# 模型初始化
# ============================================================================

# 初始化各种预测模型
# 注意：这些模型实例在应用启动时创建，全局复用
# 优点：避免每次请求都创建新实例，提高性能
# 线程安全：Prophet/LSTM/ARIMA 模型在预测时会创建新实例，因此是线程安全的

prophet_model = ProphetModel()      # Prophet 模型（推荐，最常用）
lstm_model = LSTMModel()            # LSTM 模型（需要大量数据）
arima_model = ARIMAModel()          # ARIMA 模型（传统方法）
ensemble_model = EnsembleModel()    # 集成模型（综合多个模型）

# 初始化工具类
preprocessor = DataPreprocessor()   # 数据预处理器：清洗、格式化、填充缺失值
evaluator = ModelEvaluator()        # 模型评估器：计算准确度指标


# ============================================================================
# API 路由定义
# ============================================================================

@app.route('/health', methods=['GET'])
def health_check():
    """
    健康检查接口
    
    功能：
        检查服务是否正常运行，用于负载均衡器和监控系统
    
    请求方式：
        GET /health
    
    返回格式：
        {
            "status": "healthy",           # 服务状态
            "timestamp": "2024-11-02T..."  # 当前时间（ISO格式）
        }
    
    使用场景：
        1. Kubernetes liveness/readiness probe
        2. 负载均衡器健康检查
        3. 监控告警系统
        4. 服务发现注册中心
    
    HTTP 状态码：
        200: 服务正常
    """
    return jsonify({
        'status': 'healthy',
        'timestamp': datetime.now().isoformat()
    })


@app.route('/api/forecast', methods=['POST'])
def forecast():
    """
    时间序列预测接口（核心功能）
    
    功能：
        根据历史流量数据，预测未来一段时间的流量趋势
        支持多种预测模型，返回预测值和置信区间
    
    请求方式：
        POST /api/forecast
        Content-Type: application/json
    
    请求体格式：
        {
            "interface_id": 1,                    # 接口ID（必填）
            "historical_data": [                  # 历史数据数组（必填）
                {
                    "timestamp": "2024-01-01 00:00:00",  # 时间戳
                    "request_count": 100                  # 请求次数
                },
                ...  # 至少需要 48 个数据点（2天），建议 168 个（7天）
            ],
            "forecast_hours": 24,                 # 预测未来多少小时（可选，默认24）
            "model_type": "prophet",              # 模型类型（可选，默认prophet）
                                                  # 选项: prophet/lstm/arima/ensemble
            "confidence_level": 0.95              # 置信区间水平（可选，默认0.95）
                                                  # 选项: 0.80, 0.90, 0.95, 0.99
        }
    
    返回格式：
        {
            "interface_id": 1,
            "model_type": "prophet",
            "forecast_hours": 24,
            "predictions": [                      # 预测结果数组
                {
                    "timestamp": "2024-11-03 00:00:00",
                    "predicted_value": 120,       # 预测值（点估计）
                    "confidence_lower": 95,       # 置信区间下界
                    "confidence_upper": 145       # 置信区间上界
                },
                ...
            ],
            "accuracy_metrics": {                 # 模型准确度指标
                "mse": 123.45,                    # 均方误差
                "rmse": 11.11,                    # 均方根误差
                "mae": 8.5,                       # 平均绝对误差
                "mape": 7.2,                      # 平均绝对百分比误差
                "r2_score": 0.92,                 # R²得分
                "accuracy_score": 0.928           # 准确度评分
            },
            "generated_at": "2024-11-02T..."      # 生成时间
        }
    
    HTTP 状态码：
        200: 预测成功
        400: 请求参数错误（数据不足、格式错误等）
        500: 服务器内部错误（模型训练失败等）
    
    使用示例：
        curl -X POST http://localhost:5000/api/forecast \\
          -H "Content-Type: application/json" \\
          -d '{"interface_id": 1, "historical_data": [...], "forecast_hours": 24}'
    
    注意事项：
        1. 历史数据至少需要 48 小时，建议 7 天以上
        2. 数据应该是连续的小时级数据
        3. Prophet 模型适合大多数场景（推荐）
        4. LSTM 模型需要更多数据（至少 200 个数据点）
        5. 预测时间越长，不确定性越大
    """
    try:
        # ====================================================================
        # 步骤 1: 获取请求数据
        # ====================================================================
        data = request.json
        logger.info(f"收到预测请求: interface_id={data.get('interface_id')}")

        # ====================================================================
        # 步骤 2: 参数验证
        # ====================================================================
        
        # 验证历史数据是否存在
        if not data.get('historical_data'):
            logger.warning("请求缺少历史数据")
            return jsonify({'error': '历史数据不能为空'}), 400

        # 提取请求参数
        interface_id = data.get('interface_id')              # 接口ID
        historical_data = data.get('historical_data')        # 历史数据列表
        forecast_hours = data.get('forecast_hours', 24)      # 预测小时数，默认24
        model_type = data.get('model_type', 'prophet')       # 模型类型，默认prophet
        confidence_level = data.get('confidence_level', 0.95) # 置信区间，默认0.95

        logger.info(f"预测参数: model={model_type}, hours={forecast_hours}, "
                   f"confidence={confidence_level}, data_points={len(historical_data)}")

        # ====================================================================
        # 步骤 3: 数据预处理
        # ====================================================================
        
        # 使用数据预处理器处理原始数据
        # 预处理包括：
        # - 格式转换（JSON → DataFrame）
        # - 时间戳解析
        # - 缺失值填充
        # - 异常值处理
        # - 数据排序
        df = preprocessor.prepare_data(historical_data)
        
        logger.info(f"数据预处理完成: {len(df)} 个有效数据点")

        # 验证数据量是否充足
        # 最少需要 48 小时（2天）的数据
        # 说明：Prophet 需要足够的历史数据来学习周期性模式
        if len(df) < 48:
            logger.warning(f"历史数据不足: {len(df)} < 48")
            return jsonify({
                'error': f'历史数据不足，至少需要48小时数据，当前只有{len(df)}小时'
            }), 400

        # ====================================================================
        # 步骤 4: 模型选择和预测
        # ====================================================================
        
        # 根据请求的模型类型选择相应的预测模型
        # 支持的模型：prophet, lstm, arima, ensemble
        
        if model_type == 'prophet':
            # Facebook Prophet 模型（推荐）
            # 优点：易用、准确、适合大多数场景
            # 适用：有明显周期性的流量数据
            logger.info("使用 Prophet 模型进行预测")
            predictions = prophet_model.predict(df, forecast_hours, confidence_level)
            
        elif model_type == 'lstm':
            # LSTM 深度学习模型
            # 优点：可以捕捉复杂的非线性模式
            # 缺点：需要更多数据（至少200个数据点）、训练时间较长
            # 适用：复杂波动模式、大量历史数据可用
            logger.info("使用 LSTM 模型进行预测")
            predictions = lstm_model.predict(df, forecast_hours, confidence_level)
            
        elif model_type == 'arima':
            # ARIMA 传统统计模型
            # 优点：经典方法、可解释性强
            # 缺点：对非平稳数据处理复杂、参数调优困难
            # 适用：相对平稳的时间序列
            logger.info("使用 ARIMA 模型进行预测")
            predictions = arima_model.predict(df, forecast_hours, confidence_level)
            
        elif model_type == 'ensemble':
            # 集成学习模型（组合多个模型）
            # 优点：综合多个模型的优点、准确度通常最高
            # 缺点：计算时间较长
            # 适用：对准确度要求高的场景
            logger.info("使用 Ensemble 集成模型进行预测")
            predictions = ensemble_model.predict(df, forecast_hours, confidence_level)
            
        else:
            # 不支持的模型类型
            logger.warning(f"不支持的模型类型: {model_type}")
            return jsonify({
                'error': f'不支持的模型类型: {model_type}',
                'supported_models': ['prophet', 'lstm', 'arima', 'ensemble']
            }), 400

        logger.info(f"预测完成: 生成 {len(predictions)} 个预测点")

        # ====================================================================
        # 步骤 5: 模型评估
        # ====================================================================
        
        # 计算模型准确度指标（基于历史数据的交叉验证）
        # 评估指标包括：
        # - MSE: 均方误差
        # - RMSE: 均方根误差
        # - MAE: 平均绝对误差
        # - MAPE: 平均绝对百分比误差
        # - R²: 决定系数
        # - Accuracy: 准确度评分
        accuracy_metrics = evaluator.evaluate_model(df, model_type)
        
        logger.info(f"模型评估完成: accuracy={accuracy_metrics.get('accuracy_score', 'N/A')}")

        # ====================================================================
        # 步骤 6: 构建响应
        # ====================================================================
        
        # 组装返回给客户端的完整响应数据
        response = {
            'interface_id': interface_id,                    # 接口ID
            'model_type': model_type,                        # 使用的模型类型
            'forecast_hours': forecast_hours,                # 预测的小时数
            'predictions': predictions,                      # 预测结果数组
            'accuracy_metrics': accuracy_metrics,            # 模型准确度指标
            'generated_at': datetime.now().isoformat()       # 生成时间（ISO格式）
        }

        logger.info(f"预测成功: interface_id={interface_id}, 预测点数={len(predictions)}")
        
        # 返回 JSON 响应，HTTP 状态码 200
        return jsonify(response)

    except Exception as e:
        # ====================================================================
        # 异常处理
        # ====================================================================
        
        # 捕获所有异常，记录详细错误日志
        # 包括完整的堆栈跟踪，便于调试
        logger.error(f"预测失败: {str(e)}")
        logger.error(f"详细错误信息:\n{traceback.format_exc()}")
        
        # 返回错误响应，HTTP 状态码 500
        return jsonify({
            'error': str(e),
            'error_type': type(e).__name__
        }), 500


@app.route('/api/batch-forecast', methods=['POST'])
def batch_forecast():
    """
    批量预测接口（支持多个接口同时预测）
    
    功能：
        一次请求预测多个接口的流量，提高效率
        适用于需要同时预测多个接口的场景
    
    请求方式：
        POST /api/batch-forecast
        Content-Type: application/json
    
    请求体格式：
        {
            "interfaces": [                        # 接口数组（必填）
                {
                    "interface_id": 1,             # 接口ID
                    "historical_data": [...]       # 历史数据
                },
                {
                    "interface_id": 2,
                    "historical_data": [...]
                }
            ],
            "forecast_hours": 24,                  # 预测小时数（可选，默认24）
            "model_type": "prophet"                # 模型类型（可选，默认prophet）
        }
    
    返回格式：
        {
            "total": 2,                            # 总数
            "success": 1,                          # 成功数
            "failed": 1,                           # 失败数
            "results": [                           # 结果数组
                {
                    "interface_id": 1,
                    "status": "success",           # 状态：success/failed
                    "predictions": [...]           # 预测结果（成功时）
                },
                {
                    "interface_id": 2,
                    "status": "failed",
                    "error": "历史数据不足"        # 错误信息（失败时）
                }
            ]
        }
    
    HTTP 状态码：
        200: 请求成功（部分或全部接口预测成功）
        500: 服务器内部错误
    
    使用场景：
        1. 定时任务批量预测所有接口
        2. 一次性获取多个接口的预测结果
        3. 提高API调用效率，减少网络开销
    
    注意事项：
        1. 单次请求建议不超过100个接口
        2. 部分接口失败不影响其他接口
        3. 返回结果按请求顺序排列
        4. 建议异步处理大批量预测
    """
    try:
        # ====================================================================
        # 步骤 1: 获取请求数据
        # ====================================================================
        data = request.json
        interfaces = data.get('interfaces', [])        # 接口数组
        forecast_hours = data.get('forecast_hours', 24) # 预测小时数
        model_type = data.get('model_type', 'prophet')  # 模型类型

        logger.info(f"收到批量预测请求: 接口数量={len(interfaces)}, "
                   f"model={model_type}, hours={forecast_hours}")

        # 验证接口数组不为空
        if not interfaces:
            return jsonify({'error': '接口列表不能为空'}), 400

        # ====================================================================
        # 步骤 2: 循环处理每个接口
        # ====================================================================
        results = []  # 存储所有接口的预测结果

        for idx, interface_data in enumerate(interfaces):
            try:
                # 提取单个接口的数据
                interface_id = interface_data.get('interface_id')
                historical_data = interface_data.get('historical_data')

                logger.info(f"处理第 {idx + 1}/{len(interfaces)} 个接口: id={interface_id}")

                # 数据预处理
                df = preprocessor.prepare_data(historical_data)

                # 验证数据量
                if len(df) < 48:
                    # 数据不足，记录失败结果
                    logger.warning(f"接口 {interface_id} 数据不足: {len(df)} < 48")
                    results.append({
                        'interface_id': interface_id,
                        'status': 'failed',
                        'error': f'历史数据不足，需要至少48小时，当前{len(df)}小时'
                    })
                    continue  # 跳过此接口，继续处理下一个

                # ============================================================
                # 执行预测
                # ============================================================
                # 根据模型类型选择预测方法
                if model_type == 'ensemble':
                    # 集成模型（准确度最高但耗时较长）
                    predictions = ensemble_model.predict(df, forecast_hours)
                else:
                    # 默认使用 Prophet 模型（速度快、准确度高）
                    predictions = prophet_model.predict(df, forecast_hours)

                # 预测成功，添加到结果列表
                results.append({
                    'interface_id': interface_id,
                    'status': 'success',
                    'predictions': predictions,
                    'data_points': len(df)  # 记录使用的数据点数
                })

                logger.info(f"接口 {interface_id} 预测成功: {len(predictions)} 个预测点")

            except Exception as e:
                # 单个接口预测失败，记录错误但不影响其他接口
                logger.error(f"接口 {interface_data.get('interface_id')} 预测失败: {str(e)}")
                results.append({
                    'interface_id': interface_data.get('interface_id'),
                    'status': 'failed',
                    'error': str(e),
                    'error_type': type(e).__name__
                })

        # ====================================================================
        # 步骤 3: 构建汇总响应
        # ====================================================================
        
        # 统计成功和失败数量
        success_count = sum(1 for r in results if r['status'] == 'success')
        failed_count = sum(1 for r in results if r['status'] == 'failed')

        logger.info(f"批量预测完成: 总数={len(interfaces)}, "
                   f"成功={success_count}, 失败={failed_count}")

        # 返回汇总结果
        return jsonify({
            'total': len(interfaces),              # 总接口数
            'success': success_count,              # 成功数
            'failed': failed_count,                # 失败数
            'success_rate': round(success_count / len(interfaces) * 100, 2) if interfaces else 0,  # 成功率
            'results': results,                    # 详细结果
            'generated_at': datetime.now().isoformat()
        })

    except Exception as e:
        # ====================================================================
        # 全局异常处理
        # ====================================================================
        logger.error(f"批量预测失败: {str(e)}")
        logger.error(f"详细错误信息:\n{traceback.format_exc()}")
        return jsonify({'error': str(e)}), 500


@app.route('/api/evaluate', methods=['POST'])
def evaluate():
    """
    模型评估接口（评估预测准确度）
    
    功能：
        评估指定模型在历史数据上的预测准确度
        用于模型选择、参数调优、性能监控
    
    请求方式：
        POST /api/evaluate
        Content-Type: application/json
    
    请求体格式：
        {
            "historical_data": [                   # 历史数据（必填）
                {"timestamp": "...", "request_count": 100},
                ...
            ],
            "model_type": "prophet"                # 模型类型（可选，默认prophet）
        }
    
    返回格式：
        {
            "model_type": "prophet",               # 使用的模型
            "metrics": {                           # 评估指标
                "mse": 123.45,                     # 均方误差（越小越好）
                "rmse": 11.11,                     # 均方根误差（越小越好）
                "mae": 8.5,                        # 平均绝对误差（越小越好）
                "mape": 7.2,                       # 平均绝对百分比误差（越小越好）
                "r2_score": 0.92,                  # R²决定系数（越接近1越好）
                "accuracy_score": 0.928            # 准确度评分（0-1，越大越好）
            }
        }
    
    评估指标说明：
        1. MSE (Mean Squared Error): 均方误差
           - 计算方式：预测值与真实值差的平方的平均
           - 优点：惩罚大误差
           - 缺点：单位是原始单位的平方，不直观
        
        2. RMSE (Root Mean Squared Error): 均方根误差
           - 计算方式：MSE 的平方根
           - 优点：单位与原始数据相同，更直观
           - 示例：RMSE=10 表示平均偏差约10个请求
        
        3. MAE (Mean Absolute Error): 平均绝对误差
           - 计算方式：预测值与真实值差的绝对值的平均
           - 优点：直观、对异常值不敏感
           - 示例：MAE=8 表示平均偏差8个请求
        
        4. MAPE (Mean Absolute Percentage Error): 平均绝对百分比误差
           - 计算方式：|预测值 - 真实值| / 真实值 的平均（百分比）
           - 优点：归一化指标，便于不同规模数据比较
           - 示例：MAPE=7.2 表示平均偏差7.2%
        
        5. R² Score: 决定系数
           - 取值范围：-∞ 到 1
           - 1：完美预测
           - 0：等同于预测平均值
           - <0：比预测平均值还差
           - 示例：R²=0.92 表示模型解释了92%的数据变化
        
        6. Accuracy Score: 准确度评分
           - 计算方式：1 - MAPE/100
           - 取值范围：0 到 1
           - 示例：0.928 表示92.8%的准确度
    
    HTTP 状态码：
        200: 评估成功
        400: 请求参数错误
        500: 服务器内部错误
    
    使用场景：
        1. 模型选择：对比不同模型的准确度
        2. 参数调优：测试不同参数配置的效果
        3. 性能监控：定期评估模型表现
        4. 质量保证：上线前验证模型准确度
    
    注意事项：
        1. 评估基于历史数据的交叉验证
        2. 需要足够的历史数据（至少48小时）
        3. 评估会消耗一定的计算时间
        4. 评估结果仅供参考，实际效果取决于数据质量
    """
    try:
        # ====================================================================
        # 步骤 1: 获取请求数据
        # ====================================================================
        data = request.json
        historical_data = data.get('historical_data')
        model_type = data.get('model_type', 'prophet')

        logger.info(f"收到模型评估请求: model={model_type}, "
                   f"data_points={len(historical_data) if historical_data else 0}")

        # 验证历史数据
        if not historical_data:
            return jsonify({'error': '历史数据不能为空'}), 400

        # ====================================================================
        # 步骤 2: 数据预处理
        # ====================================================================
        df = preprocessor.prepare_data(historical_data)
        
        logger.info(f"评估数据预处理完成: {len(df)} 个有效数据点")

        # 验证数据量
        if len(df) < 48:
            return jsonify({
                'error': f'历史数据不足，至少需要48小时数据，当前只有{len(df)}小时'
            }), 400

        # ====================================================================
        # 步骤 3: 执行模型评估
        # ====================================================================
        
        # 使用评估器计算模型准确度指标
        # 评估方法：时间序列交叉验证
        # - 将数据分为训练集和测试集
        # - 在训练集上训练模型
        # - 在测试集上验证预测准确度
        # - 计算各种误差指标
        metrics = evaluator.evaluate_model(df, model_type)
        
        logger.info(f"模型评估完成: RMSE={metrics.get('rmse', 'N/A')}, "
                   f"MAPE={metrics.get('mape', 'N/A')}%, "
                   f"R²={metrics.get('r2_score', 'N/A')}")

        # ====================================================================
        # 步骤 4: 返回评估结果
        # ====================================================================
        return jsonify({
            'model_type': model_type,              # 评估的模型类型
            'metrics': metrics,                    # 详细的评估指标
            'data_points': len(df),                # 使用的数据点数
            'evaluation_date': datetime.now().isoformat()  # 评估时间
        })

    except Exception as e:
        # ====================================================================
        # 异常处理
        # ====================================================================
        logger.error(f"模型评估失败: {str(e)}")
        logger.error(f"详细错误信息:\n{traceback.format_exc()}")
        return jsonify({
            'error': str(e),
            'error_type': type(e).__name__
        }), 500


# ============================================================================
# 应用启动入口
# ============================================================================

if __name__ == '__main__':
    """
    启动 Flask 开发服务器
    
    配置说明：
        - host='0.0.0.0': 监听所有网络接口（允许外部访问）
        - port=5000: 监听端口号 5000
        - debug=False: 生产模式（不开启调试模式）
    
    生产环境部署建议：
        不要直接使用 Flask 内置服务器，应使用：
        1. Gunicorn: gunicorn -w 4 -b 0.0.0.0:5000 app:app
        2. uWSGI: uwsgi --http 0.0.0.0:5000 --module app:app --processes 4
        3. Docker + Gunicorn（推荐）
    
    性能优化建议：
        1. 使用多进程/多线程（Gunicorn workers）
        2. 使用 Nginx 反向代理
        3. 启用 Redis 缓存预测结果
        4. 使用异步任务队列（Celery）处理长时间预测
    """
    logger.info("=" * 60)
    logger.info("启动 Python 时间序列预测服务")
    logger.info("服务地址: http://0.0.0.0:5000")
    logger.info("健康检查: http://0.0.0.0:5000/health")
    logger.info("API文档: 请参考代码注释")
    logger.info("=" * 60)
    
    # 启动 Flask 应用
    app.run(host='0.0.0.0', port=5000, debug=False)